package com.licq.ldoc.parser.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.licq.ldoc.domain.Attribute;
import com.licq.ldoc.domain.AttributeType;
import com.licq.ldoc.domain.AttributeTypeConverter;
import com.licq.ldoc.domain.Occurrencies;
import com.licq.ldoc.domain.AttributeType.ArrayAttributeType;
import com.licq.ldoc.domain.AttributeType.CollectionAttributeType;
import com.licq.ldoc.domain.AttributeType.MapAttributeType;
import com.licq.ldoc.support.bean.KeyValue;
import com.licq.ldoc.utils.ClassUtils;

/**
 * 
 * @author liujian
 *
 */
public class AttributeParser extends AbstractParser<Attribute> {
	
	private Field field;
	
	private AttributeTypeConverter converter = new AttributeTypeConverter();
	
	public AttributeParser(Field field) {
		this.field = field;
	}
	
	public static Attribute parse(Field field) {
		return new AttributeParser(field).doParse();
	}

	@Override
	public Attribute doParse() {
		Occurrencies defaultOccurs = null;
		if (ClassUtils.isWrapper(field.getType()) 
				|| ClassUtils.isMapper(field.getType())) {
			defaultOccurs = Occurrencies.ANY;
		} else {
			defaultOccurs = Occurrencies.ZERO_OR_ONCE;
		}
		Attribute attribute = new Attribute(field.getName(), getAttributeType(), defaultOccurs);
		parseAnnotations(attribute);
		parseIfEnum(attribute);
		return attribute;
	}

	private void parseIfEnum(Attribute attribute) {
		Class<?> clazz = field.getType();
		List<Object[]> enumConstants = new ArrayList<Object[]>();
		if (ClassUtils.isEnum(clazz) || ClassUtils.isEnumWrapper(field)
				|| ClassUtils.isEnumMapper(field)) {
			if (ClassUtils.isEnum(clazz)) {
				enumConstants.add(clazz.getEnumConstants());
			} else if (ClassUtils.isEnumWrapper(field)) {
				enumConstants.add(ClassUtils.getWrapperNestedType(field).getEnumConstants());
			} else {
				KeyValue<Class<?>, Class<?>> nestedClassPair = ClassUtils.getMapperNestedType(field);
				Class<?> kClazz = nestedClassPair.getKey();
				Class<?> vClazz = nestedClassPair.getValue();
				if (ClassUtils.isEnum(kClazz)) {
					enumConstants.add(kClazz.getEnumConstants());
				}
				if (ClassUtils.isEnum(vClazz)) {
					enumConstants.add(vClazz.getEnumConstants());
				}
			}
			for (Object[] constants : enumConstants) {
				String[] enumConstantNames = new String[constants.length];
				for (int i = 0; i < constants.length; i++) {
					enumConstantNames[i] = ((Enum<?>) constants[i]).name();
				}
				attribute.appendComments("{" + StringUtils.join(enumConstantNames, ", ") + "}");
			}
		}
	}

	private void parseAnnotations(Attribute attribute) {
		Annotation[] annotations = field.getAnnotations();
		AnnotationParsees.parse(attribute, annotations);
	}

	private AttributeType getAttributeType() {
		AttributeType attributeType = null;
		final Class<?> clazz = field.getType();
		if (ClassUtils.isMapper(clazz)) {
			KeyValue<Class<?>, Class<?>> nestedClassPair = ClassUtils.getMapperNestedType(field);
			Class<?> keyClazz = nestedClassPair.getKey();
			Class<?> valueClazz = nestedClassPair.getValue();
			AttributeType keyType = converter.convert(ClassUtils.isEnum(keyClazz) ? Enum.class 
					: keyClazz);
			AttributeType valueType = converter.convert(ClassUtils.isEnum(valueClazz) ? Enum.class 
					: valueClazz);
			attributeType = new MapAttributeType(keyType, valueType);
		} else {
			Class<?> innerClazz = clazz;
			if (ClassUtils.isWrapper(clazz)) {
				innerClazz = ClassUtils.getWrapperNestedType(field);
			}
			attributeType = converter.convert(ClassUtils.isEnum(innerClazz) ? Enum.class 
					: innerClazz);
			if (Collection.class.isAssignableFrom(clazz)) {
				attributeType = new CollectionAttributeType(attributeType);
			} else if (clazz.isArray()) {
				attributeType = new ArrayAttributeType(attributeType);
			}
		}
    	return attributeType;
	}

}
